package com.automannn.dubbo.withoutSpring.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.*;
import org.apache.dubbo.remoting.zookeeper.ZookeeperClient;
import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter;
import org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.apache.dubbo.common.constants.CommonConstants.*;
import static org.apache.dubbo.common.constants.RegistryConstants.*;

/**
 * @author automannn
 * @time 2021/7/26
 */
public class DubboUtils {
    private static final Logger logger = LoggerFactory.getLogger(DubboUtils.class);
    public static final Map<String, Object> serviceMap = new HashMap<>();

    /**
     * 取得Dubbo bean
     *
     * @param clazz
     * @param version
     * @param tag     可以不传 不传的话取system环境变量
     * @param <T>
     * @return
     */
    public static <T> T getService(Class<T> clazz, String version, String... tag) {
        if (clazz == null || StringUtils.isBlank(version)) {
            throw new IllegalArgumentException("DubboUtils#getService():参数不能为空.");
        }
        StringBuilder sb = new StringBuilder();
        String key = sb.append(clazz.getName()).append(":").append(version).toString();
        Object objService = serviceMap.get(key);
        if (objService != null && !StringUtils.equals(objService.getClass().getName(), clazz.getName())) {
            throw new IllegalArgumentException("DubboUtils#getService():返回值类型不符.期望:" + clazz.getName() +
                    ",实际:" + objService.getClass().getName());
        }
        if (serviceMap.containsKey(key)) {
            return (T) objService;
        }
        //首次使用时，创建轻量级客户端引用
        ReferenceConfig reference = createLightReferenceConfig("dubbo-generic-automan-practice-consumer","zookeeper://localhost:2181","practice");

        // 设置tag：用于灰度发布、线上程序调试
        String tagEnv = getTag(tag);
        if (StringUtils.isNotBlank(tagEnv)) {
            ConsumerConfig consumerConfig = new ConsumerConfig();
            consumerConfig.setTag(tagEnv);
            reference.setConsumer(consumerConfig);
        }
        reference.setVersion(version);
        // 弱类型接口名
        reference.setInterface(clazz);
        // 声明为泛化接口
        reference.setGeneric("false");

        //获取到客户端引用，此实例变得很重，需要缓存起来
        objService = reference.get();
        serviceMap.put(key, objService);
        return (T) objService;
    }

    /**
     * 取得Dubbo Bean
     *
     * @param clazz
     * @param methodName
     * @param version
     * @param paramsClassName
     * @param params
     * @param tag             可以不传 不传的话取system环境变量
     * @return
     */
    public static Object callGenericService(String clazz, String methodName, String version, String[] paramsClassName, Object[] params, String... tag) {
        if (StringUtils.isBlank(clazz) || StringUtils.isBlank(methodName)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():参数不能为空.packageName=" + clazz + ",methodName=" + methodName);
        }
        if ((paramsClassName != null && params == null) || (paramsClassName == null && params != null)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():参数设置错误.paramsClassName=" + clazz + ",params=" + params);
        }
        if ((paramsClassName != null && params != null) && (paramsClassName.length != params.length)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():参数设置错误.paramsClassName=" + clazz + ",params=" + params);
        }
        StringBuilder sb = new StringBuilder();
        String key = sb.append(clazz.toString()).append(".").append(methodName).append(":").append(version).append(":gen").toString();
        Object objService = serviceMap.get(key);
        if (objService != null && !(objService instanceof GenericService)) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():返回值类型不符.期望:GenericService,实际:" + objService.getClass().getName());
        }
        if (serviceMap.containsKey(key) && objService == null) {
            throw new IllegalArgumentException("DubboUtils#callGenericService():获取远程bean失败.");
        }

        if (objService == null) {
            ReferenceConfig reference = createLightReferenceConfig("dubbo-generic-automan-practice-consumer","zookeeper://localhost:2181","practice");

            // 设置tag：用于灰度发布、线上程序调试
            String tagEnv = getTag(tag);
            if (StringUtils.isNotBlank(tagEnv)) {
                ConsumerConfig consumerConfig = new ConsumerConfig();
                consumerConfig.setTag(tagEnv);
                reference.setConsumer(consumerConfig);
            }
            reference.setVersion(version);
            // 弱类型接口名
            reference.setInterface(clazz);
            //设置服务分组
            reference.setGroup("practice");

            // 声明为泛化接口
            reference.setGeneric("true");

            objService = reference.get();
            serviceMap.put(key, objService);
        }

        // 基本类型以及Date,List,Map等不需要转换，直接调用
        GenericService genericService = (GenericService) objService;
        return genericService.$invoke(methodName, paramsClassName, params);
    }

    //创建轻量级消费者引用
    private static ReferenceConfig createLightReferenceConfig(String appName,String registryCenter,String namespace) {
        // 普通编码配置方式
        ApplicationConfig application = new ApplicationConfig();
        // 客户端应用名：可以任意取名
        application.setName(appName);
        application.setLogger("slf4j");
        application.setQosPort(22223);

        // 连接注册中心配置
        RegistryConfig registry1 = new RegistryConfig();
        registry1.setAddress(registryCenter);
        registry1.setGroup(namespace);
        List<RegistryConfig> registries = new ArrayList<>();
        registries.add(registry1);

        // 引用远程服务
        // 当获取到实际的客户端连接后，该实例变得很重，里面封装了所有与注册中心及服务提供方连接，需要缓存
        // 此时，尚处于一个轻量级的实例
        ReferenceConfig reference = new ReferenceConfig();
        reference.setApplication(application);
        reference.setRegistries(registries);
        reference.setCheck(false);
        reference.setGroup("practice");
        return reference;
    }

    private static String getTag(String... tag) {
        String tagEnv = System.getProperty("dubbo.provider.tag");
        if (tag != null && tag.length > 0 && StringUtils.isNotBlank(tag[0])) {
            tagEnv = tag[0];
        }
        return tagEnv;
    }

    private static boolean registerService(URL url){
        ZookeeperTransporter zookeeperTransporter = new CuratorZookeeperTransporter();
        ZookeeperClient zkClient=zookeeperTransporter.connect(url);
        zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));

        return true;
    };

    /*暂时先不设置参数*/
    public static  boolean  registerService(Class clazzImpl,Object serviceImpl,String appName,String registryCenter,String namespace){
        ServiceConfig serviceConfig = new ServiceConfig();
        serviceConfig.setInterface(clazzImpl);
        serviceConfig.setInterface(clazzImpl.getName());
        serviceConfig.setVersion("1.0");
        serviceConfig.setGroup("practice");
        serviceConfig.setRef(serviceImpl);

        // 普通编码配置方式
        ApplicationConfig application = new ApplicationConfig();
        // 客户端应用名：可以任意取名
        application.setName(appName);
        application.setLogger("slf4j");

        // 连接注册中心配置
        RegistryConfig registry1 = new RegistryConfig();
        registry1.setAddress(registryCenter);
        registry1.setGroup(namespace);
        List<RegistryConfig> registries = new ArrayList<>();
        registries.add(registry1);

        serviceConfig.setRegistries(registries);
        serviceConfig.setApplication(application);
        serviceConfig.export();
        return true;
    };

    private static String toUrlPath(URL url) {
        return toCategoryPath(url) + PATH_SEPARATOR + URL.encode(url.toFullString());
    }

    private static String toCategoryPath(URL url) {
        return toServicePath(url) + PATH_SEPARATOR + url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
    }

    private static String toServicePath(URL url) {
        String name = url.getServiceInterface();
        if (ANY_VALUE.equals(name)) {
            return toRootPath(url);
        }
        return toRootDir(url) + URL.encode(name);
    }

    private static String toRootDir(URL url) {
        String root;
        String group = url.getParameter(GROUP_KEY, "automanDubbo");
        if (!group.startsWith(PATH_SEPARATOR)) {
            group = PATH_SEPARATOR + group;
        }
        root = group;
        if (root.equals(PATH_SEPARATOR)) {
            return root;
        }
        return root + PATH_SEPARATOR;
    }

    private static String toRootPath(URL url) {
        String root;
        String group = url.getParameter(GROUP_KEY, "automanDubbo");
        if (!group.startsWith(PATH_SEPARATOR)) {
            group = PATH_SEPARATOR + group;
        }
        root = group;
        return root;
    }
}
